'''
Created on Jan 22, 2012

@author: jonathan
'''

import string

from google.appengine.api import datastore_errors

import webapp2

class ParameterLoadError(Exception): pass
    
class Parameter:
    def __init__(self, name, required=False, default=None):
        self.name = str(name) if name is not None else None
        self.required = bool(required)
        self.default = None
        if default is not None:
            try:
                self.default = self.Validate(default)
            except ParameterLoadError:
                raise ValueError()
        
        self._ValidateName()
        
    def Validate(self, value):
        return value
    
    def _ValidateName(self):
        if self.name is None:
            raise ValueError()
        if len(self.name) <= 0:
            raise ValueError()
        if not self.name[0] in string.ascii_letters:
            raise ValueError()
        
def NoneSafeMin(a, b):
    return a if b is None else b if a is None else min(int(a), int(b))

def NoneSafeMax(a, b):
    return a if b is None else b if a is None else max(int(a), int(b))
        
class Integer(Parameter):
    _typeLowerBound = None
    _typeUpperBound = None
    
    def __init__(self, name, required=False, default=None, lowerBound=None, upperBound=None):
        self._lowerBound = NoneSafeMax(self._typeLowerBound, lowerBound)
        self._upperBound = NoneSafeMin(self._typeUpperBound, upperBound)
        Parameter.__init__(self, name, required, default)
        
    def Validate(self, value):
        if value is None:
            return None
        try:
            value = int(value)
        except:
            raise ParameterLoadError()
        
        if (self._upperBound is not None and value > int(self._upperBound)) or \
            (self._lowerBound is not None and value < int(self._lowerBound)):
            raise ParameterLoadError()
        return value

class Integer32(Integer):
    _typeLowerBound = -2147483648
    _typeUpperBound = 2147483647

class UnsignedInteger32(Integer):
    _typeLowerBound = 0
    _typeUpperBound = 4294967295
    
class Integer64(Integer):
    _typeLowerBound = -9223372036854775808
    _typeUpperBound = 9223372036854775807
    
class UnsignedInteger64(Integer):
    _typeLowerBound = 0
    _typeUpperBound = 18446744073709551615
    
class RealNumber(Parameter):
    def Validate(self, value):
        try:
            return float(value)
        except:
            raise ParameterLoadError()
        
class String(Parameter):
    def Validate(self, value):
        if value is None:
            return None
        try:
            return str(value)
        except:
            raise ParameterLoadError()

class Parameters:
    def __setitem__(self, key, value):
        setattr(self, key, value)
    
    def __getitem__(self, key):
        getattr(self, key)

class APIRequest(webapp2.RequestHandler):
    _parameterDefinitions = []
    _parameters = Parameters()
    
    def dispatch(self):
        try:
            self._LoadParameters()
            self._SetHeaders()
        except Exception, e:
            return self.handle_exception(e, self.app.debug)
        
        super(APIRequest, self).dispatch()
            
    def handle_exception(self, exception, debug):
        if isinstance(exception, ParameterLoadError) or isinstance(exception, datastore_errors.Error):
            self.response.set_status(400)
        else:
            raise
            
    def _LoadParameters(self):
        for p in self._parameterDefinitions:
            if p.name in self.request.params:
                self._parameters[p.name] = p.Validate(self.request.params[p.name])
            elif not p.required:
                self._parameters[p.name] = p.default
            else:
                raise ParameterLoadError()
            
    def _SetHeaders(self):
        pass
    
class XMLAPIRequest(APIRequest):
    def _SetHeaders(self):
        self.response.headers.add('Content-Type', "text/xml; charset=utf-8")
